import java.util.Arrays;

/**
 * @program: data_structures_algorithms
 * @description: 堆排序
 * @author: lld
 * @create: 2020-11-04 18:23
 **/
public class HeapSort {
    /*
     *           7
     *          / \
     *         4   9
     *        / \
     *       1   5
     *   对应数组：7 4 9 1 5
     *   下标i的父节点：(i-1)/2
     *   i的子节点：2*i+1,2*i+2
     */

    /**
     * “下沉”调整 ，将普通二叉树转化为大顶堆
     *
     * @param array       待调整的堆
     * @param parentIndex 要“下沉”的父节点
     * @param length      堆的有效大小
     *                    小顶堆array[childIndex + 1] < array[childIndex]    temp <= array[childIndex]
     */
    public static void downAdjust(int[] array, int parentIndex, int length) {
        // temp 保存父节点值，用于最后的赋值
        int temp = array[parentIndex];//0 1 2 3
        int childIndex = 2 * parentIndex + 1;//左孩子，p=0,c=1;p=1,c=3
        while (childIndex < length) {
            // 如果有右孩子，且右孩子小于左孩子的值，则定位到右孩子
            if (childIndex + 1 < length && array[childIndex + 1] > array[childIndex]) {
                childIndex++;//定位到右孩子
            } // 如果父节点大于任何一个孩子的值，则直接跳出
            if (temp >= array[childIndex])//右孩子
                break;
            // 无须真正交换，单向赋值即可
            array[parentIndex] = array[childIndex];//让父节点=子节点的值
            parentIndex = childIndex;
            childIndex = 2 * childIndex + 1;//如果子节点也是作为父节点，继续比较
        }
        array[parentIndex] = temp;
    }


    /**
     * 构建堆
     *
     * @param array 待调整的堆
     */

    public static void buildHeap(int[] array) {
        // 从最后一个非叶子节点开始(array.length - 2) / 2，依次做“下沉”调整
        for (int i = (array.length - 1) / 2; i >= 0; i--) {
            downAdjust(array, i, array.length);
        }
    }

    public static void heapSort(int[] array) {
        //循环删除堆顶元素，移到集合尾部，调整堆产生新的堆顶
        for (int i = array.length - 1; i > 0; i--) {//7 4 9 1 5,5 4 9 1 7
            // 最后1个元素和第1个元素进行交换
            int temp = array[i];
            array[i] = array[0];
            array[0] = temp;
            // “下沉”调整最大堆，因为只交换首尾，直接从顶到i调整,后面排好的不用调整
            downAdjust(array, 0, i);
            System.out.println(Arrays.toString(array));
        }
    }

    public static void main(String[] args) {
//        int[] array = new int[] {7,1,3,10,5,2,8,9,6};
        int[] array = new int[]{7, 4, 9, 1, 5};
        buildHeap(array);//构造大顶堆
        System.out.println("第一次调整的大顶堆：" + Arrays.toString(array));
        heapSort(array);
        System.out.println("排序后的：" + Arrays.toString(array));
//        //测试执行效率
//        int[] bigArray1=new int[10000000];
//        for(int i=0;i<bigArray1.length;i++){
//            bigArray1[i]=(int)(Math.random()*1000000000);
//        }
//        long start = System.currentTimeMillis();
//        buildHeap(bigArray1);
//        heapSort(bigArray1);
//        long end = System.currentTimeMillis();
//        System.out.println("10000000个数据排序时间(毫秒):"+(end-start));//3770
    }
}
